<template>
  <view class="v-page" :style="{ background: background }">
    <view
      v-if="$slots.header"
      class="v-page-header"
      :style="{ background: headerBackground }"
    >
      <slot name="header" />
    </view>
    <scroll-view
      v-if="!disableScroll"
      class="v-page-content"
      :class="{ 'empty-content': empty }"
      scroll-y
      :scroll-top="scrollTop"
      :scroll-into-view="scrollIntoView"
      :scroll-with-animation="scrollWithAnimation"
      :refresher-enabled="modelValue !== undefined"
      :refresher-triggered="modelValue"
      :enable-back-to-top="enableBackToTop"
      @refresherrefresh="$emit('refresh')"
      @scroll="$emit('scroll', $event)"
      @scrolltoupper="$emit('toupper')"
      @scrolltolower="$emit('tolower')"
    >
      <template v-if="modelValue">
        <!-- #ifndef MP-ALIPAY -->
        <v-loading-block />
        <!-- #endif -->
      </template>
      <template v-else>
        <template v-if="empty">
          <v-empty :text="emptyText" :oss-src="emptyOssSrc" :src="emptySrc" />
        </template>
        <template v-else>
          <view class="v-page-top" :style="{ height: paddingTop }" />
          <slot v-if="!loading && !modelValue" />
          <v-loading-block v-else-if="loading" />
          <view
            class="v-page-bottom"
            :class="{ 'has-footer': footer ?? $slots.footer }"
          />
        </template>
      </template>
    </scroll-view>
    <view v-else class="v-page-content">
      <slot />
    </view>
    <view
      v-if="!loading && !modelValue && (footer ?? $slots.footer)"
      class="v-page-footer"
      :style="{ background: footerBackground }"
    >
      <slot name="footer" />
      <view class="v-page-footer-bottom" />
    </view>
    <slot name="popup" />
  </view>
</template>

<script setup lang="ts">
import type { Component } from 'vue';

const props = withDefaults(
  defineProps<{
    modelValue?: boolean;
    loading?: boolean;
    empty?: boolean;
    emptyText?: string;
    emptySrc?: string;
    emptyOssSrc?: string;
    paddingTop?: string;
    background?: string;
    headerBackground?: string;
    footerBackground?: string;
    refreshBackground?: string;
    footer?: boolean;
    disableScroll?: boolean;
    scrollTop?: number;
    scrollIntoView?: string;
    scrollWithAnimation?: boolean;
    enableBackToTop?: boolean;
  }>(),
  {
    modelValue: undefined,
    loading: false,
    empty: false,
    emptyText: '暂无数据',
    emptySrc: '',
    emptyOssSrc: '',
    paddingTop: '0',
    background: '#fff',
    headerBackground: '',
    footerBackground: '',
    refreshBackground: '#ffffff',
    footer: undefined,
    disableScroll: false,
    scrollTop: undefined,
    scrollIntoView: undefined,
    scrollWithAnimation: true,
    enableBackToTop: true
  }
);

const emits = defineEmits<{
  'update:model-value': [val: boolean];
  refresh: [];
  scroll: [val: ScrollEvent];
  toupper: [];
  tolower: [];
}>();

const getPageHeight = async (components: Component) => {
  await new Promise((resolve) => nextTick(() => resolve(true)));
  const query = uni.createSelectorQuery().in(components);
  return await new Promise<number>((resolve) => {
    query
      .select('.v-page-content')
      .boundingClientRect((res) => {
        const result = res as UniApp.NodeInfo;
        resolve(result?.height ?? 0);
      })
      .exec();
  });
};

// #ifdef MP-ALIPAY
onPullDownRefresh(() => {
  // https://opendocs.alipay.com/mini/api/pmhkbb?pathHash=6ae94b4e#Bugs%20%26%20Tips
  // 支付宝小程序明说了有1秒延迟，这边只使用该动画效果作为提示，直接结束
  emits('update:model-value', true);
  my.stopPullDownRefresh({
    complete: () => {
      // 同步这一秒，否则看上去调用接口不同步
      setTimeout(() => emits('refresh'), 1500);
    }
  });
});

onMounted(() => {
  my.setBackgroundColor({
    backgroundColor: props.refreshBackground,
    backgroundColorTop: props.refreshBackground,
    backgroundColorBottom: props.refreshBackground
  });
});
// #endif

defineExpose({ getPageHeight });
</script>

<style lang="scss" scoped>
.v-page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  .v-page-header {
    position: relative;
    z-index: 0;
    height: fit-content;
  }
  .v-page-content {
    position: relative;
    z-index: 0;
    flex: 1;
    // overflow: hidden;
    min-height: 0;
    height: 0;
    .v-page-bottom {
      height: constant(safe-area-inset-bottom);
      height: env(safe-area-inset-bottom);
      &.has-footer {
        height: 30rpx;
      }
    }
    &.empty-content {
      display: flex;
      flex-direction: column;
    }
  }
  .v-page-footer {
    position: relative;
    z-index: 0;
    height: fit-content;
    padding-bottom: constant(safe-area-inset-bottom);
    padding-bottom: env(safe-area-inset-bottom);
  }
}
</style>
